#include "graphics/mesh.h"
#include "graphics/graphicsmanager.h"
#include "graphics/materialmanager.h"
#include "graphics/material.h"
#include "graphics/submesh.h"
#include "graphics/indexbuffer.h"
#include "graphics/vertexbuffer.h"
#include "graphics/renderer.h"

namespace dw
{
namespace graphics
{

Mesh::Mesh() : _vertexBuffer(0), _indexBuffer(0)
{
}

Mesh::~Mesh()
{
	for (unsigned i = 0; i < _subMeshes.size(); ++i)
		removeSubMesh(i);

	GraphicsManager::getInstance().getRenderer().releaseIndexBuffer(_indexBuffer);
	GraphicsManager::getInstance().getRenderer().releaseVertexBuffer(_vertexBuffer);
}

Object::Type Mesh::getType() const
{
	return MESH;
}

SubMesh* Mesh::createSubMesh(primitive::types primitiveType, 
	const uint32 indexStart, 
	const uint32 numIndices, 
	const uint32 vertexIndexStart, 
	const uint32 vertexIndexEnd, 
	const std::string& materialName)
{
	SubMesh* subMesh = new SubMesh(primitiveType, indexStart, numIndices, vertexIndexStart, vertexIndexEnd, materialName);
	addSubMesh(subMesh);
	return subMesh;
}

void Mesh::addSubMesh(SubMesh* subMesh)
{
	dwAssert(subMesh != NULL, "Invalid parameter: subMesh == NULL");
	
	_subMeshes.push_back(subMesh);
}

void Mesh::removeSubMesh(const uint32 i)
{
	dwAssert(i < _subMeshes.size(), "Invalid parameter: i is out of range");

	delete _subMeshes[i];
	_subMeshes.erase(_subMeshes.begin() + i);
}

void Mesh::optimize()
{
	// DW_NOT_IMPLEMENTED;

	/*if (!_indexBuffer)
		return ;

	vector<unsigned short> strippedIndices;

	for (unsigned i = 0; i < _subMeshes.size(); ++i)
	{
		PrimitiveGroup* primGroups = 0;
		unsigned short numGroups;
		unsigned offset = _subMeshes[i]->getIndexStart() * _subMeshes[i]->getIndexSize();		

		GenerateStrips(_indexBuffer->lock(IBuffer::BA_READ_ONLY) + offset, _subMeshes[i]->getIndexCount(), &primGroups, &numGroups);

		numSubMeshIndices = primGroups[0].numIndices;
		for (unsigned j = 0; j < primGroups[0].numIndices; ++j)
			strippedIndices.push_back(primGroups[0].indices[j]);
	}*/
	
}

void Mesh::render() const
{
	static float ambientDefault[] = { 0.2f, 0.2f, 0.2f, 1.0f };
	static float diffuseDefault[] = { 0.8f, 0.8f, 0.8f, 1.0f };
	static float specularDefault[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	static float emissionDefault[] = { 0.0f, 0.0f, 0.0f, 1.0f };
	static float shininessDefault = 0;
	Renderer& renderer(GraphicsManager::getInstance().getRenderer());

	renderer.setVertexBuffer(_vertexBuffer);
	renderer.setIndexBuffer(_indexBuffer);
	renderer.drawBox(_boundingBox);
	for (SubMeshConstIterator iter = _subMeshes.begin(); iter != _subMeshes.end(); ++iter)
	{
		if (!(**iter).getMaterialName().empty())
		{
			Material* material = MaterialManager::getInstance().getMaterial((**iter).getMaterialName());
			unsigned numPasses = material->begin();
			for (unsigned i = 0; i < numPasses; ++i)
			{
				material->beginPass(i);
				renderer.drawIndexedPrimitive((**iter).getPrimitiveType(), (**iter).getIndexStart(), (**iter).getIndexCount(), (**iter).getVertexIndexStart(), (**iter).getVertexIndexEnd());
				material->endPass(i);
			}
		}
		else
		{
			glEnable(GL_COLOR_MATERIAL);
			renderer.drawIndexedPrimitive((**iter).getPrimitiveType(), (**iter).getIndexStart(), (**iter).getIndexCount(), (**iter).getVertexIndexStart(), (**iter).getVertexIndexEnd());
			//glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambientDefault);
			//glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuseDefault);
			//glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specularDefault);
			//glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, emissionDefault);
			//glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininessDefault);
			glDisable(GL_COLOR_MATERIAL);
		}
		
	}
}

Mesh* Mesh::sphere(const float radius, const int slices, const int stacks)
{
	return 0;
}

Mesh* Mesh::cylinder(const float radius, const float height)
{
	return 0;
}

Mesh* Mesh::torus(const float radius)
{
	return 0;
}

std::ostream& operator<< (std::ostream& os, const Mesh& mesh)
{
	unsigned numSubMeshes = static_cast< unsigned >(mesh._subMeshes.size());

	os << dynamic_cast< const Object& >(mesh);
	
	// os << *(mesh._vertexBuffer);
	// os << *(mesh._indexBuffer);
	os.write(reinterpret_cast< const char* >(&numSubMeshes), sizeof (unsigned));
	for (unsigned i = 0; i < numSubMeshes; ++i)
		os << *(mesh._subMeshes[i]);
	return os;
}

std::istream& operator>> (std::istream& is, Mesh& mesh)
{
	unsigned numSubMeshes = 0;

	is >> dynamic_cast< Object& >(mesh);

	// mesh._vertexBuffer	= GraphicsManager::getInstance().getRenderer().createVertexBuffer(VertexFormat(), 0, IBuffer::BU_STATIC);
	// mesh._vertexBuffer->load(is);
	// mesh._indexBuffer		=	GraphicsManager::getInstance().getRenderer().createIndexBuffer(IIndexBuffer::UNSIGNED_INT, 0, IIndexBuffer::TRIANGLES, IBuffer::BU_STATIC);
	// mesh._indexBuffer->load(is);
	is.read(reinterpret_cast< char* >(&numSubMeshes), sizeof (unsigned));
	mesh._subMeshes.resize(numSubMeshes, 0);
	for (unsigned i = 0; i < numSubMeshes; ++i)
	{
		mesh._subMeshes[i] = new SubMesh();
		is >> *(mesh._subMeshes[i]);
	}
	return is;
}

} // namespace graphics
} // namespace dw